/*
 * Copyright (c) 2018-2022, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 *
 * Contains: Advanced Host Controller Interface for SATA
 * 
 * Change Logs:
 * Date           Author        Notes
 * 2022-08-13     JasonHu       first version
 */

#ifndef __DRIVER_AHCI_H__
#define __DRIVER_AHCI_H__

#include <nxos_mini.h>

enum AHCI_FIS_TYPE
{
    FIS_TYPE_REG_H2D    = 0x27,    // Register FIS - host to device
    FIS_TYPE_REG_D2H    = 0x34,    // Register FIS - device to host
    FIS_TYPE_DMA_ACT    = 0x39,    // DMA activate FIS - device to host
    FIS_TYPE_DMA_SETUP  = 0x41,    // DMA setup FIS - bidirectional
    FIS_TYPE_DATA       = 0x46,    // Data FIS - bidirectional
    FIS_TYPE_BIST       = 0x58,    // BIST activate FIS - bidirectional
    FIS_TYPE_PIO_SETUP  = 0x5F,    // PIO setup FIS - device to host
    FIS_TYPE_DEV_BITS   = 0xA1,    // Set device bits FIS - device to host
};

struct fis_reg_host_to_device
{
    NX_U8 fis_type;

    NX_U8 pmport:4;
    NX_U8 reserved0:3;
    NX_U8 c:1;

    NX_U8 command;
    NX_U8 feature_l;

    NX_U8 lba0;
    NX_U8 lba1;
    NX_U8 lba2;
    NX_U8 device;

    NX_U8 lba3;
    NX_U8 lba4;
    NX_U8 lba5;
    NX_U8 feature_h;

    NX_U8 count_l;
    NX_U8 count_h;
    NX_U8 icc;
    NX_U8 control;

    NX_U8 reserved1[4];
} NX_PACKED;

struct fis_reg_device_to_host
{
    NX_U8 fis_type;

    NX_U8 pmport:4;
    NX_U8 reserved0:2;
    NX_U8 interrupt:1;
    NX_U8 reserved1:1;

    NX_U8 status;
    NX_U8 error;

    NX_U8 lba0;
    NX_U8 lba1;
    NX_U8 lba2;
    NX_U8 device;

    NX_U8 lba3;
    NX_U8 lba4;
    NX_U8 lba5;
    NX_U8 reserved2;

    NX_U8 count_l;
    NX_U8 count_h;
    NX_U8 reserved3[2];

    NX_U8 reserved4[4];
} NX_PACKED;

struct fis_data
{
    NX_U8 fis_type;
    NX_U8 pmport:4;
    NX_U8 reserved0:4;
    NX_U8 reserved1[2];

    NX_U32 data[1];
} NX_PACKED;

struct fis_pio_setup
{
    NX_U8 fis_type;

    NX_U8 pmport:4;
    NX_U8 reserved0:1;
    NX_U8 direction:1;
    NX_U8 interrupt:1;
    NX_U8 reserved1:1;

    NX_U8 status;
    NX_U8 error;

    NX_U8 lba0;
    NX_U8 lba1;
    NX_U8 lba2;
    NX_U8 device;

    NX_U8 lba3;
    NX_U8 lba4;
    NX_U8 lba5;
    NX_U8 reserved2;

    NX_U8 count_l;
    NX_U8 count_h;
    NX_U8 reserved3;
    NX_U8 e_status;

    NX_U16 transfer_count;
    NX_U8 reserved4[2];
} NX_PACKED;

struct fis_dma_setup
{
    NX_U8 fis_type;

    NX_U8 pmport:4;
    NX_U8 reserved0:1;
    NX_U8 direction:1;
    NX_U8 interrupt:1;
    NX_U8 auto_activate:1;

    NX_U8 reserved1[2];

    NX_U64 dma_buffer_id;

    NX_U32 reserved2;

    NX_U32 dma_buffer_offset;

    NX_U32 transfer_count;

    NX_U32 reserved3;
} NX_PACKED;

struct fis_dev_bits
{
    volatile NX_U8 fis_type;

    volatile NX_U8 pmport:4;
    volatile NX_U8 reserved0:2;
    volatile NX_U8 interrupt:1;
    volatile NX_U8 notification:1;

    volatile NX_U8 status;
    volatile NX_U8 error;

    volatile NX_U32 protocol;
} NX_PACKED;

struct hba_port
{
    volatile NX_U32 command_list_base_l;
    volatile NX_U32 command_list_base_h;
    volatile NX_U32 fis_base_l;
    volatile NX_U32 fis_base_h;
    volatile NX_U32 interrupt_status;
    volatile NX_U32 interrupt_enable;
    volatile NX_U32 command;
    volatile NX_U32 reserved0;
    volatile NX_U32 task_file_data;
    volatile NX_U32 signature;
    volatile NX_U32 sata_status;
    volatile NX_U32 sata_control;
    volatile NX_U32 sata_error;
    volatile NX_U32 sata_active;
    volatile NX_U32 command_issue;
    volatile NX_U32 sata_notification;
    volatile NX_U32 fis_based_switch_control;
    volatile NX_U32 reserved1[11];
    volatile NX_U32 vendor[4];
} NX_PACKED;

struct hba_memory
{
    volatile NX_U32 capability;
    volatile NX_U32 global_host_control;
    volatile NX_U32 interrupt_status;
    volatile NX_U32 port_implemented;
    volatile NX_U32 version;
    volatile NX_U32 ccc_control;
    volatile NX_U32 ccc_ports;
    volatile NX_U32 em_location;
    volatile NX_U32 em_control;
    volatile NX_U32 ext_capabilities;
    volatile NX_U32 bohc;

    volatile NX_U8 reserved[0xA0 - 0x2C];

    volatile NX_U8 vendor[0x100 - 0xA0];

    volatile struct hba_port ports[1];
} NX_PACKED;

struct hba_received_fis
{
    volatile struct fis_dma_setup fis_ds;
    volatile NX_U8 pad0[4];

    volatile struct fis_pio_setup fis_ps;
    volatile NX_U8 pad1[12];

    volatile struct fis_reg_device_to_host fis_r;
    volatile NX_U8 pad2[4];

    volatile struct fis_dev_bits fis_sdb;
    volatile NX_U8 ufis[64];
    volatile NX_U8 reserved[0x100 - 0xA0];
} NX_PACKED;

struct hba_command_header
{
    NX_U8 fis_length:5;
    NX_U8 atapi:1;
    NX_U8 write:1;
    NX_U8 prefetchable:1;

    NX_U8 reset:1;
    NX_U8 bist:1;
    NX_U8 clear_busy_upon_r_ok:1;
    NX_U8 reserved0:1;
    NX_U8 pmport:4;

    NX_U16 prdt_len;

    volatile NX_U32 prdb_count;

    NX_U32 command_table_base_l;
    NX_U32 command_table_base_h;

    NX_U32 reserved1[4];
} NX_PACKED;

struct hba_prdt_entry
{
    NX_U32 data_base_l;
    NX_U32 data_base_h;
    NX_U32 reserved0;

    NX_U32 byte_count:22;
    NX_U32 reserved1:9;
    NX_U32 interrupt_on_complete:1;
} NX_PACKED;

struct hba_command_table
{
    NX_U8 command_fis[64];
    NX_U8 acmd[16];
    NX_U8 reserved[48];
    struct hba_prdt_entry prdt_entries[1];
} NX_PACKED;

#define HBA_COMMAND_HEADER_NUM 32

struct ata_identify
{
    NX_U16 ata_device;
    NX_U16 dont_care[48];
    NX_U16 cap0;
    NX_U16 cap1;
    NX_U16 obs[2];
    NX_U16 free_fall;
    NX_U16 dont_care_2[8];
    NX_U16 dma_mode0;
    NX_U16 pio_modes;
    NX_U16 dont_care_3[4];
    NX_U16 additional_supported;
    NX_U16 rsv1[6];
    NX_U16 serial_ata_cap0;
    NX_U16 rsv2;

    NX_U16 serial_ata_features;
    NX_U16 serial_ata_features_enabled;

    NX_U16 maj_ver;
    NX_U16 min_ver;

    NX_U16 features0;
    NX_U16 features1;
    NX_U16 features2;
    NX_U16 features3;
    NX_U16 features4;
    NX_U16 features5;

    NX_U16 udma_modes;
    NX_U16 dont_care_4[11];
    NX_U64 lba48_addressable_sectors;
    NX_U16 wqewqe[2];
    NX_U16 ss_1;
    NX_U16 rrrrr[4];
    NX_U32 ss_2;
    /* ...and more */
};

#define HBA_PxCMD_ST  (1 << 0)
#define HBA_PxCMD_FRE (1 << 4)
#define HBA_PxCMD_FR  (1 << 14)
#define HBA_PxCMD_CR  (1 << 15)

#define HBA_GHC_AHCI_ENABLE         (1 << 31)
#define HBA_GHC_INTERRUPT_ENABLE    (1 << 1)
#define HBA_GHC_RESET               (1 << 0)

#define ATA_CMD_IDENTIFY 0xEC

#define ATA_DEV_BUSY    0x80
#define ATA_DEV_DRQ     0x08
#define ATA_DEV_ERR     0x01

#define ATA_CMD_READ_DMA_EX  0x25
#define ATA_CMD_WRITE_DMA_EX 0x35

#define PRDT_MAX_COUNT   0x1000

#define PRDT_MAX_ENTRIES 65535

#define ATA_TFD_TIMEOUT  1000000
#define AHCI_CMD_TIMEOUT 1000000

#define ATA_SECTOR_SIZE  512

#define AHCI_DEFAULT_INT 0

#define SATA_SIG_ATA     0x00000101    // SATA drive
#define SATA_SIG_ATAPI   0xEB140101    // SATAPI drive
#define SATA_SIG_SEMB    0xC33C0101    // Enclosure management bridge
#define SATA_SIG_PM      0x96690101    // Port multiplier

enum AHCI_DEVICE_TYPE
{
    AHCI_DEV_NULL = 0,
    AHCI_DEV_SATA,
    AHCI_DEV_SEMB,
    AHCI_DEV_PM,
    AHCI_DEV_SATAPI
};

#define HBA_PORT_IPM_ACTIVE  1
#define HBA_PORT_DET_PRESENT 3

#endif /* __DRIVER_AHCI_H__ */
